{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import print_function\n",
    "\n",
    "import math\n",
    "\n",
    "from IPython import display\n",
    "from matplotlib import cm\n",
    "from matplotlib import gridspec\n",
    "from matplotlib import pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from sklearn import metrics\n",
    "import tensorflow as tf\n",
    "from tensorflow.python.data import Dataset\n",
    "\n",
    "tf.logging.set_verbosity(tf.logging.ERROR)\n",
    "pd.options.display.max_rows = 10\n",
    "pd.options.display.float_format = '{:.1f}'.format\n",
    "\n",
    "california_housing_dataframe = pd.read_csv(\"https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv\", sep=\",\")\n",
    "\n",
    "california_housing_dataframe = california_housing_dataframe.reindex(\n",
    "    np.random.permutation(california_housing_dataframe.index))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def preprocess_features(california_housing_dataframe):\n",
    "  \"\"\"Prepares input features from California housing data set.\n",
    "\n",
    "  Args:\n",
    "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
    "      from the California housing data set.\n",
    "  Returns:\n",
    "    A DataFrame that contains the features to be used for the model, including\n",
    "    synthetic features.\n",
    "  \"\"\"\n",
    "  selected_features = california_housing_dataframe[\n",
    "    [\"latitude\",\n",
    "     \"longitude\",\n",
    "     \"housing_median_age\",\n",
    "     \"total_rooms\",\n",
    "     \"total_bedrooms\",\n",
    "     \"population\",\n",
    "     \"households\",\n",
    "     \"median_income\"]]\n",
    "  processed_features = selected_features.copy()\n",
    "  # Create a synthetic feature.\n",
    "  processed_features[\"rooms_per_person\"] = (\n",
    "    california_housing_dataframe[\"total_rooms\"] /\n",
    "    california_housing_dataframe[\"population\"])\n",
    "  return processed_features\n",
    "\n",
    "def preprocess_targets(california_housing_dataframe):\n",
    "  \"\"\"Prepares target features (i.e., labels) from California housing data set.\n",
    "\n",
    "  Args:\n",
    "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
    "      from the California housing data set.\n",
    "  Returns:\n",
    "    A DataFrame that contains the target feature.\n",
    "  \"\"\"\n",
    "  output_targets = pd.DataFrame()\n",
    "  # Create a boolean categorical feature representing whether the\n",
    "  # medianHouseValue is above a set threshold.\n",
    "  output_targets[\"median_house_value_is_high\"] = (\n",
    "    california_housing_dataframe[\"median_house_value\"] > 265000).astype(float)\n",
    "  return output_targets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training examples summary:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>latitude</th>\n",
       "      <th>longitude</th>\n",
       "      <th>housing_median_age</th>\n",
       "      <th>total_rooms</th>\n",
       "      <th>total_bedrooms</th>\n",
       "      <th>population</th>\n",
       "      <th>households</th>\n",
       "      <th>median_income</th>\n",
       "      <th>rooms_per_person</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "      <td>12000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>35.6</td>\n",
       "      <td>-119.6</td>\n",
       "      <td>28.7</td>\n",
       "      <td>2629.1</td>\n",
       "      <td>537.6</td>\n",
       "      <td>1424.3</td>\n",
       "      <td>499.6</td>\n",
       "      <td>3.9</td>\n",
       "      <td>2.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>2.1</td>\n",
       "      <td>2.0</td>\n",
       "      <td>12.5</td>\n",
       "      <td>2160.8</td>\n",
       "      <td>421.8</td>\n",
       "      <td>1159.3</td>\n",
       "      <td>385.7</td>\n",
       "      <td>1.9</td>\n",
       "      <td>1.1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>32.5</td>\n",
       "      <td>-124.3</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.5</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>33.9</td>\n",
       "      <td>-121.8</td>\n",
       "      <td>18.0</td>\n",
       "      <td>1454.0</td>\n",
       "      <td>295.0</td>\n",
       "      <td>783.8</td>\n",
       "      <td>280.0</td>\n",
       "      <td>2.6</td>\n",
       "      <td>1.5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>34.2</td>\n",
       "      <td>-118.5</td>\n",
       "      <td>29.0</td>\n",
       "      <td>2115.0</td>\n",
       "      <td>432.0</td>\n",
       "      <td>1164.0</td>\n",
       "      <td>407.5</td>\n",
       "      <td>3.5</td>\n",
       "      <td>1.9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>37.7</td>\n",
       "      <td>-118.0</td>\n",
       "      <td>37.0</td>\n",
       "      <td>3153.0</td>\n",
       "      <td>646.0</td>\n",
       "      <td>1719.0</td>\n",
       "      <td>603.0</td>\n",
       "      <td>4.8</td>\n",
       "      <td>2.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>42.0</td>\n",
       "      <td>-114.6</td>\n",
       "      <td>52.0</td>\n",
       "      <td>37937.0</td>\n",
       "      <td>6445.0</td>\n",
       "      <td>35682.0</td>\n",
       "      <td>6082.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>55.2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       latitude  longitude  housing_median_age  total_rooms  total_bedrooms  \\\n",
       "count   12000.0    12000.0             12000.0      12000.0         12000.0   \n",
       "mean       35.6     -119.6                28.7       2629.1           537.6   \n",
       "std         2.1        2.0                12.5       2160.8           421.8   \n",
       "min        32.5     -124.3                 1.0          2.0             1.0   \n",
       "25%        33.9     -121.8                18.0       1454.0           295.0   \n",
       "50%        34.2     -118.5                29.0       2115.0           432.0   \n",
       "75%        37.7     -118.0                37.0       3153.0           646.0   \n",
       "max        42.0     -114.6                52.0      37937.0          6445.0   \n",
       "\n",
       "       population  households  median_income  rooms_per_person  \n",
       "count     12000.0     12000.0        12000.0           12000.0  \n",
       "mean       1424.3       499.6            3.9               2.0  \n",
       "std        1159.3       385.7            1.9               1.1  \n",
       "min           3.0         1.0            0.5               0.0  \n",
       "25%         783.8       280.0            2.6               1.5  \n",
       "50%        1164.0       407.5            3.5               1.9  \n",
       "75%        1719.0       603.0            4.8               2.3  \n",
       "max       35682.0      6082.0           15.0              55.2  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validation examples summary:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>latitude</th>\n",
       "      <th>longitude</th>\n",
       "      <th>housing_median_age</th>\n",
       "      <th>total_rooms</th>\n",
       "      <th>total_bedrooms</th>\n",
       "      <th>population</th>\n",
       "      <th>households</th>\n",
       "      <th>median_income</th>\n",
       "      <th>rooms_per_person</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "      <td>5000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>35.6</td>\n",
       "      <td>-119.6</td>\n",
       "      <td>28.4</td>\n",
       "      <td>2678.6</td>\n",
       "      <td>543.8</td>\n",
       "      <td>1442.2</td>\n",
       "      <td>505.2</td>\n",
       "      <td>3.9</td>\n",
       "      <td>2.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>2.1</td>\n",
       "      <td>2.0</td>\n",
       "      <td>12.7</td>\n",
       "      <td>2225.0</td>\n",
       "      <td>420.8</td>\n",
       "      <td>1120.0</td>\n",
       "      <td>381.6</td>\n",
       "      <td>1.9</td>\n",
       "      <td>1.4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>32.5</td>\n",
       "      <td>-124.2</td>\n",
       "      <td>1.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>9.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>0.5</td>\n",
       "      <td>0.1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>33.9</td>\n",
       "      <td>-121.8</td>\n",
       "      <td>18.0</td>\n",
       "      <td>1474.0</td>\n",
       "      <td>301.0</td>\n",
       "      <td>804.0</td>\n",
       "      <td>286.0</td>\n",
       "      <td>2.6</td>\n",
       "      <td>1.5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>34.2</td>\n",
       "      <td>-118.5</td>\n",
       "      <td>28.0</td>\n",
       "      <td>2152.0</td>\n",
       "      <td>437.0</td>\n",
       "      <td>1175.0</td>\n",
       "      <td>413.5</td>\n",
       "      <td>3.5</td>\n",
       "      <td>1.9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>37.7</td>\n",
       "      <td>-118.0</td>\n",
       "      <td>37.0</td>\n",
       "      <td>3146.5</td>\n",
       "      <td>656.0</td>\n",
       "      <td>1725.5</td>\n",
       "      <td>608.0</td>\n",
       "      <td>4.8</td>\n",
       "      <td>2.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>42.0</td>\n",
       "      <td>-114.3</td>\n",
       "      <td>52.0</td>\n",
       "      <td>32054.0</td>\n",
       "      <td>5290.0</td>\n",
       "      <td>15507.0</td>\n",
       "      <td>5050.0</td>\n",
       "      <td>15.0</td>\n",
       "      <td>52.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       latitude  longitude  housing_median_age  total_rooms  total_bedrooms  \\\n",
       "count    5000.0     5000.0              5000.0       5000.0          5000.0   \n",
       "mean       35.6     -119.6                28.4       2678.6           543.8   \n",
       "std         2.1        2.0                12.7       2225.0           420.8   \n",
       "min        32.5     -124.2                 1.0         15.0             3.0   \n",
       "25%        33.9     -121.8                18.0       1474.0           301.0   \n",
       "50%        34.2     -118.5                28.0       2152.0           437.0   \n",
       "75%        37.7     -118.0                37.0       3146.5           656.0   \n",
       "max        42.0     -114.3                52.0      32054.0          5290.0   \n",
       "\n",
       "       population  households  median_income  rooms_per_person  \n",
       "count      5000.0      5000.0         5000.0            5000.0  \n",
       "mean       1442.2       505.2            3.9               2.0  \n",
       "std        1120.0       381.6            1.9               1.4  \n",
       "min           9.0         3.0            0.5               0.1  \n",
       "25%         804.0       286.0            2.6               1.5  \n",
       "50%        1175.0       413.5            3.5               1.9  \n",
       "75%        1725.5       608.0            4.8               2.3  \n",
       "max       15507.0      5050.0           15.0              52.0  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training targets summary:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>median_house_value_is_high</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>12000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>0.2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>0.4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       median_house_value_is_high\n",
       "count                     12000.0\n",
       "mean                          0.2\n",
       "std                           0.4\n",
       "min                           0.0\n",
       "25%                           0.0\n",
       "50%                           0.0\n",
       "75%                           0.0\n",
       "max                           1.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Validation targets summary:\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>median_house_value_is_high</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>5000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>0.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>0.4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       median_house_value_is_high\n",
       "count                      5000.0\n",
       "mean                          0.3\n",
       "std                           0.4\n",
       "min                           0.0\n",
       "25%                           0.0\n",
       "50%                           0.0\n",
       "75%                           1.0\n",
       "max                           1.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Choose the first 12000 (out of 17000) examples for training.\n",
    "training_examples = preprocess_features(california_housing_dataframe.head(12000))\n",
    "training_targets = preprocess_targets(california_housing_dataframe.head(12000))\n",
    "\n",
    "# Choose the last 5000 (out of 17000) examples for validation.\n",
    "validation_examples = preprocess_features(california_housing_dataframe.tail(5000))\n",
    "validation_targets = preprocess_targets(california_housing_dataframe.tail(5000))\n",
    "\n",
    "# Double-check that we've done the right thing.\n",
    "print(\"Training examples summary:\")\n",
    "display.display(training_examples.describe())\n",
    "print(\"Validation examples summary:\")\n",
    "display.display(validation_examples.describe())\n",
    "\n",
    "print(\"Training targets summary:\")\n",
    "display.display(training_targets.describe())\n",
    "print(\"Validation targets summary:\")\n",
    "display.display(validation_targets.describe())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_input_fn(features, targets, batch_size=1, shuffle=True, num_epochs=None):\n",
    "    \"\"\"Trains a linear regression model of one feature.\n",
    "  \n",
    "    Args:\n",
    "      features: pandas DataFrame of features\n",
    "      targets: pandas DataFrame of targets\n",
    "      batch_size: Size of batches to be passed to the model\n",
    "      shuffle: True or False. Whether to shuffle the data.\n",
    "      num_epochs: Number of epochs for which data should be repeated. None = repeat indefinitely\n",
    "    Returns:\n",
    "      Tuple of (features, labels) for next data batch\n",
    "    \"\"\"\n",
    "  \n",
    "    # Convert pandas data into a dict of np arrays.\n",
    "    features = {key:np.array(value) for key,value in dict(features).items()}                                            \n",
    " \n",
    "    # Construct a dataset, and configure batching/repeating\n",
    "    ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit\n",
    "    ds = ds.batch(batch_size).repeat(num_epochs)\n",
    "    \n",
    "    # Shuffle the data, if specified\n",
    "    if shuffle:\n",
    "      ds = ds.shuffle(10000)\n",
    "    \n",
    "    # Return the next batch of data\n",
    "    features, labels = ds.make_one_shot_iterator().get_next()\n",
    "    return features, labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_quantile_based_buckets(feature_values, num_buckets):\n",
    "  quantiles = feature_values.quantile(\n",
    "    [(i+1.)/(num_buckets + 1.) for i in range(num_buckets)])\n",
    "  return [quantiles[q] for q in quantiles.keys()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def construct_feature_columns():\n",
    "  \"\"\"Construct the TensorFlow Feature Columns.\n",
    "\n",
    "  Returns:\n",
    "    A set of feature columns\n",
    "  \"\"\"\n",
    "\n",
    "  bucketized_households = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"households\"),\n",
    "    boundaries=get_quantile_based_buckets(training_examples[\"households\"], 10))\n",
    "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"longitude\"),\n",
    "    boundaries=get_quantile_based_buckets(training_examples[\"longitude\"], 50))\n",
    "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"latitude\"),\n",
    "    boundaries=get_quantile_based_buckets(training_examples[\"latitude\"], 50))\n",
    "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"housing_median_age\"),\n",
    "    boundaries=get_quantile_based_buckets(\n",
    "      training_examples[\"housing_median_age\"], 10))\n",
    "  bucketized_total_rooms = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"total_rooms\"),\n",
    "    boundaries=get_quantile_based_buckets(training_examples[\"total_rooms\"], 10))\n",
    "  bucketized_total_bedrooms = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"total_bedrooms\"),\n",
    "    boundaries=get_quantile_based_buckets(training_examples[\"total_bedrooms\"], 10))\n",
    "  bucketized_population = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"population\"),\n",
    "    boundaries=get_quantile_based_buckets(training_examples[\"population\"], 10))\n",
    "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"median_income\"),\n",
    "    boundaries=get_quantile_based_buckets(training_examples[\"median_income\"], 10))\n",
    "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
    "    tf.feature_column.numeric_column(\"rooms_per_person\"),\n",
    "    boundaries=get_quantile_based_buckets(\n",
    "      training_examples[\"rooms_per_person\"], 10))\n",
    "\n",
    "  long_x_lat = tf.feature_column.crossed_column(\n",
    "    set([bucketized_longitude, bucketized_latitude]), hash_bucket_size=1000)\n",
    "\n",
    "  feature_columns = set([\n",
    "    long_x_lat,\n",
    "    bucketized_longitude,\n",
    "    bucketized_latitude,\n",
    "    bucketized_housing_median_age,\n",
    "    bucketized_total_rooms,\n",
    "    bucketized_total_bedrooms,\n",
    "    bucketized_population,\n",
    "    bucketized_households,\n",
    "    bucketized_median_income,\n",
    "    bucketized_rooms_per_person])\n",
    "  \n",
    "  return feature_columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def model_size(estimator):\n",
    "  variables = estimator.get_variable_names()\n",
    "  size = 0\n",
    "  for variable in variables:\n",
    "    if not any(x in variable \n",
    "               for x in ['global_step',\n",
    "                         'centered_bias_weight',\n",
    "                         'bias_weight',\n",
    "                         'Ftrl']\n",
    "              ):\n",
    "      size += np.count_nonzero(estimator.get_variable_value(variable))\n",
    "  return size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_linear_classifier_model(\n",
    "    learning_rate,\n",
    "    regularization_strength,\n",
    "    steps,\n",
    "    batch_size,\n",
    "    feature_columns,\n",
    "    training_examples,\n",
    "    training_targets,\n",
    "    validation_examples,\n",
    "    validation_targets):\n",
    "  \"\"\"Trains a linear regression model.\n",
    "  \n",
    "  In addition to training, this function also prints training progress information,\n",
    "  as well as a plot of the training and validation loss over time.\n",
    "  \n",
    "  Args:\n",
    "    learning_rate: A `float`, the learning rate.\n",
    "    regularization_strength: A `float` that indicates the strength of the L1\n",
    "       regularization. A value of `0.0` means no regularization.\n",
    "    steps: A non-zero `int`, the total number of training steps. A training step\n",
    "      consists of a forward and backward pass using a single batch.\n",
    "    feature_columns: A `set` specifying the input feature columns to use.\n",
    "    training_examples: A `DataFrame` containing one or more columns from\n",
    "      `california_housing_dataframe` to use as input features for training.\n",
    "    training_targets: A `DataFrame` containing exactly one column from\n",
    "      `california_housing_dataframe` to use as target for training.\n",
    "    validation_examples: A `DataFrame` containing one or more columns from\n",
    "      `california_housing_dataframe` to use as input features for validation.\n",
    "    validation_targets: A `DataFrame` containing exactly one column from\n",
    "      `california_housing_dataframe` to use as target for validation.\n",
    "      \n",
    "  Returns:\n",
    "    A `LinearClassifier` object trained on the training data.\n",
    "  \"\"\"\n",
    "\n",
    "  periods = 7\n",
    "  steps_per_period = steps / periods\n",
    "\n",
    "  # Create a linear classifier object.\n",
    "  my_optimizer = tf.train.FtrlOptimizer(learning_rate=learning_rate, l1_regularization_strength=regularization_strength)\n",
    "  my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)\n",
    "  linear_classifier = tf.estimator.LinearClassifier(\n",
    "      feature_columns=feature_columns,\n",
    "      optimizer=my_optimizer\n",
    "  )\n",
    "  \n",
    "  # Create input functions.\n",
    "  training_input_fn = lambda: my_input_fn(training_examples, \n",
    "                                          training_targets[\"median_house_value_is_high\"], \n",
    "                                          batch_size=batch_size)\n",
    "  predict_training_input_fn = lambda: my_input_fn(training_examples, \n",
    "                                                  training_targets[\"median_house_value_is_high\"], \n",
    "                                                  num_epochs=1, \n",
    "                                                  shuffle=False)\n",
    "  predict_validation_input_fn = lambda: my_input_fn(validation_examples, \n",
    "                                                    validation_targets[\"median_house_value_is_high\"], \n",
    "                                                    num_epochs=1, \n",
    "                                                    shuffle=False)\n",
    "  \n",
    "  # Train the model, but do so inside a loop so that we can periodically assess\n",
    "  # loss metrics.\n",
    "  print(\"Training model...\")\n",
    "  print(\"LogLoss (on validation data):\")\n",
    "  training_log_losses = []\n",
    "  validation_log_losses = []\n",
    "  for period in range (0, periods):\n",
    "    # Train the model, starting from the prior state.\n",
    "    linear_classifier.train(\n",
    "        input_fn=training_input_fn,\n",
    "        steps=steps_per_period\n",
    "    )\n",
    "    # Take a break and compute predictions.\n",
    "    training_probabilities = linear_classifier.predict(input_fn=predict_training_input_fn)\n",
    "    training_probabilities = np.array([item['probabilities'] for item in training_probabilities])\n",
    "    \n",
    "    validation_probabilities = linear_classifier.predict(input_fn=predict_validation_input_fn)\n",
    "    validation_probabilities = np.array([item['probabilities'] for item in validation_probabilities])\n",
    "    \n",
    "    # Compute training and validation loss.\n",
    "    training_log_loss = metrics.log_loss(training_targets, training_probabilities)\n",
    "    validation_log_loss = metrics.log_loss(validation_targets, validation_probabilities)\n",
    "    # Occasionally print the current loss.\n",
    "    print(\"  period %02d : %0.2f\" % (period, validation_log_loss))\n",
    "    # Add the loss metrics from this period to our list.\n",
    "    training_log_losses.append(training_log_loss)\n",
    "    validation_log_losses.append(validation_log_loss)\n",
    "  print(\"Model training finished.\")\n",
    "\n",
    "  # Output a graph of loss metrics over periods.\n",
    "  plt.ylabel(\"LogLoss\")\n",
    "  plt.xlabel(\"Periods\")\n",
    "  plt.title(\"LogLoss vs. Periods\")\n",
    "  plt.tight_layout()\n",
    "  plt.plot(training_log_losses, label=\"training\")\n",
    "  plt.plot(validation_log_losses, label=\"validation\")\n",
    "  plt.legend()\n",
    "\n",
    "  return linear_classifier"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Assignment1:\n",
    "查找可同时满足以下两种限制条件的 L1 正则化强度参数：模型的参数数量不超过 600 个且验证集的对数损失函数低于 0.35。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training model...\n",
      "LogLoss (on validation data):\n",
      "  period 00 : 0.34\n",
      "  period 01 : 0.30\n",
      "  period 02 : 0.28\n",
      "  period 03 : 0.27\n",
      "  period 04 : 0.26\n",
      "  period 05 : 0.25\n",
      "  period 06 : 0.25\n",
      "Model training finished.\n",
      "Model size: 564\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "linear_classifier = train_linear_classifier_model(\n",
    "    learning_rate=0.1,\n",
    "    # TWEAK THE REGULARIZATION VALUE BELOW\n",
    "    regularization_strength=0.8,\n",
    "    steps=300,\n",
    "    batch_size=100,\n",
    "    feature_columns=construct_feature_columns(),\n",
    "    training_examples=training_examples,\n",
    "    training_targets=training_targets,\n",
    "    validation_examples=validation_examples,\n",
    "    validation_targets=validation_targets)\n",
    "print(\"Model size:\", model_size(linear_classifier))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
